local dm = DisplayMode ( )
dm.width = 800
dm.height = 600
dm.depth = 32
dm.windowed = true
display:set_mode ( dm )

scene = Layer ( )
display.viewport:add_child ( scene )


require ( "Box2D" )

function b2Body:OnCreate ( )
  local sprite = Sprite ( )
  scene:add_child ( sprite )

  -- attach a sprite to the body
  self.sprite = sprite
end

function b2Body:OnDestroy ( )
  scene:remove_child ( self.sprite )
  self.sprite = nil
end

function b2Body:OnUpdate ( )
  -- synchronize the sprite to the body
  local x, y = self:GetPositionXY ( 0, 0 )
  local angle = self:GetAngle ( )
  self.sprite:set_position ( x, y )
  self.sprite:set_rotation_r ( -angle )
end

function b2Body:TestPoint ( pt )
  local xf = self:GetXForm ( )

  local shape = self:GetShapeList ( )
  while shape ~= nil do
    if shape:TestPoint ( xf, pt ) == true then
      return true
    end
    shape = shape:GetNext ( )
  end

  return false
end

-- create box2D world
worldAABB = b2AABB ( )
worldAABB.lowerBound:Set ( -1000, -1000 )
worldAABB.upperBound:Set ( 1000, 1000 )
gravity = b2Vec2 ( 0, -100 )
world = b2World ( worldAABB, gravity, true )


all = {}
shapes = {}

function CreateBox ( x, y, a, w, h, d, f, r )
  local bd = b2BodyDef ( )
  bd.position:Set ( x, y )
  bd.angle = a
  bd.allowSleep = false
  bd.isSleeping = false
  local body = world:CreateBody ( bd )

  local sd = b2PolygonDef ( )
  sd:SetAsBox ( w, h )
  sd.density = d
  sd.friction = f
  sd.restitution = r
  local shape = body:CreateShape ( sd )
  body:SetMassFromShapes ( )

  body:OnCreate ( )

  body.sprite.canvas:move_to ( 0, 0 )
  body.sprite.canvas:rectangle ( w * 2, h * 2 )
  body.sprite.canvas:set_line_style ( 2, WHITE, 1 )
  body.sprite.canvas:stroke ( )

  table.insert ( shapes, shape )
  -- add to the list of bodies
  table.insert ( all, body )
  return body
end


function CreateCircle ( x, y, a, rad, d, f, r )
  local bd = b2BodyDef ( )
  bd.position:Set ( x, y )
  bd.angle = a
  bd.allowSleep = false
  bd.isSleeping = false
  local body = world:CreateBody ( bd )

  local sd = b2CircleDef ( )
  sd.radius = rad
  sd.localPosition:Set ( 0, 0 )
  sd.density = d
  sd.friction = f
  sd.restitution = r
  local shape = body:CreateShape ( sd )
  body:SetMassFromShapes ( )

  body:OnCreate ( )

  body.sprite.canvas:move_to ( 0, 0 )
  body.sprite.canvas:circle ( rad )
  body.sprite.canvas:rel_line_to ( 0, -rad )
  body.sprite.canvas:set_line_style ( 2, WHITE, 1 )
  body.sprite.canvas:stroke ( )

  table.insert ( shapes, shape )
  -- add to the list of bodies
  table.insert ( all, body )
  return body
end


CreateBox ( 0, -200, 0, 500, 10, 0, 1, 0 )
CreateBox ( 300, 0, 0, 10, 500, 0, 1, 0 )
CreateBox ( -300, 0, 0, 10, 500, 0, 1, 0 )


prop = CreateBox ( 0, 0, 0, 100, 5, 1, 1, 0 )
pj = b2RevoluteJointDef ( )
pj:Initialize ( prop, world:GetGroundBody ( ), b2Vec2 ( 0, 0 ) )
j = world:CreateJoint ( pj )
j:EnableMotor ( true )
j:SetMotorSpeed ( 5 )
j:SetMaxMotorTorque ( 15000 )

for ix = -5, 5 do
  for iy = 0, 20 do
    local x = ix * 40
    local y = iy * 40 + 500
    local a = math.rad ( math.random ( 360 ) )
    local s = math.random ( 20, 100 ) / 100 * 20

    local t = math.random ( 2 )
    if t == 1 then
      CreateBox ( x, y, a, s, s, 1, 1, 0 )
    elseif t == 2 then
      CreateCircle ( x, y, a, s, 1, 1, 0 )
    end
  end
end

accumulator = 0
timer = Timer ( )
timer:start ( 16, true )
timer.on_tick = function ( timer )
  local delta = timer:get_delta_ms ( ) / 1000
  local interval = timer.interval / 1000

  accumulator = accumulator + delta

  while accumulator > interval do
    world:Step ( interval, 10 )
    accumulator = accumulator - interval
  end

  for k, v in ipairs ( all ) do
    v:OnUpdate ( )
  end
end

--[[
selected = nil

mouse.on_press = function ( button )
  if button == MBUTTON_LEFT then
    local pt = b2Vec2 ( mouse.xaxis, mouse.yaxis )
    for i, v in pairs ( all ) do
      if v:TestPoint ( pt ) == true then
        s = v

        local mjd = b2MouseJointDef ( )
        mjd.body1 = v
        mjd.body2 = world:GetGroundBody ( )
        mjd.collideConnected = false
        mjd.target = b2Vec2 ( mouse.xaxis, mouse.yaxis )
        mjd.maxForce = 300.0 * v:GetMass ( )
        mj = world:CreateJoint ( mjd )
        break
      end
    end
  end
end

mouse.on_move = function ( dx, dy )
  if s then
    local p = s:GetPosition ( )
    local a = s:GetAngle ( )
    s:SetXForm ( b2Vec2 ( mouse.xaxis, mouse.yaxis ), a )
    --mj:SetTarget ( b2Vec2 ( mouse.xaxis, mouse.yaxis ) )
  end
end

mouse.on_release = function ( button )
  if button == MBUTTON_LEFT then
    if s then
      s = nil
    end
  end
end


]]



-- help
font = Font ( )
font:load_system ( "Courier New", 12 )
sprite = Sprite ( )
display.viewport:add_child ( sprite )
sprite.depth = -10000
sprite.canvas:set_font ( font, YELLOW )

help = true

htimer = Timer ( )
htimer:start ( 1, true )
htimer.on_tick = function ( htimer )
	local lh = font:get_height ( )
	local x = -800 / 2 + ( lh - font:get_size ( ) )
	local y = 600 / 2
	local fps = 1000 / htimer:get_delta ( )
	local c = sprite.canvas
	c:clear ( )

	if help == false then
		return
	end

	c:move_to ( x, y - lh )
	c:write ( "Real-time physics simulation" )
	c:move_to ( x, y - lh * 2 )
	c:write ( "FPS - " .. fps )
	c:move_to ( x, y - lh * 3 )
	c:write ( "Bodies - " .. world:GetBodyCount ( ) )
	c:move_to ( x, y - lh * 4 )
	c:write ( "Proxies - " .. world:GetProxyCount ( ) )
	c:move_to ( x, y - lh * 5 )
	c:write ( "KEY_F1 - toggle help" )
end


if keyboard then
	keyboard.on_press = function ( keyboard, key )
		if key == KEY_F1 then
			help = not help
		end
	end
end
